<?php
// webhook.php

// 加载环境变量
function load_env($path)
{
    if (!file_exists($path)) {
        return [];
    }

    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $env = [];

    foreach ($lines as $line) {
        if (strpos(trim($line), '#') === 0) {
            continue;
        }
        list($name, $value) = array_map('trim', explode('=', $line, 2));
        $env[$name] = trim($value, '"');
    }

    return $env;
}

$env = load_env(__DIR__ . '/.env');

// 配置日志记录
$log_file = __DIR__ . '/logs/app.log';
if (!file_exists(__DIR__ . '/logs')) {
    mkdir(__DIR__ . '/logs', 0777, true);
}

function log_message($level, $message)
{
    global $log_file;
    $date = date('Y-m-d H:i:s');
    $log_entry = "[$date] $level: $message" . PHP_EOL;
    file_put_contents($log_file, $log_entry, FILE_APPEND);
}

// 全局异常处理
set_exception_handler(function ($e) {
    log_message('ERROR', '未捕获的异常: ' . $e->getMessage());
    http_response_code(500);
    echo json_encode(['message' => 'Internal Server Error']);
    exit;
});

// 全局错误处理
set_error_handler(function ($severity, $message, $file, $line) {
    if (!(error_reporting() & $severity)) {
        // 此错误代码不在错误报告中
        return;
    }
    log_message('ERROR', "PHP 错误: [{$severity}] {$message} in {$file} on line {$line}");
    http_response_code(500);
    echo json_encode(['message' => 'Internal Server Error']);
    exit;
});

// 设置响应头
header('Content-Type: application/json');

// 获取请求头
function get_request_headers()
{
    $headers = [];
    foreach ($_SERVER as $name => $value) {
        if (substr($name, 0, 5) == 'HTTP_') {
            $header_name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
            $headers[$header_name] = $value;
        }
    }
    return $headers;
}

$headers = get_request_headers();
// 注释掉调试信息
// echo "<pre>";
// print_r($headers);  // 或使用 var_dump($headers);
// echo "</pre>";

$signature = isset($headers['X-Atomgit-Token']) ? $headers['X-Atomgit-Token'] : '';

// 验证签名
$webhookSecret = isset($env['WEBHOOK_SECRET']) ? $env['WEBHOOK_SECRET'] : '';
if ($webhookSecret !== '' && $signature !== $webhookSecret) {
    log_message('WARNING', 'Webhook 签名验证失败。');
    http_response_code(401);
    echo json_encode(['message' => 'Unauthorized']);
    exit;
}

$event = isset($headers['X-Atomgit-Event']) ? $headers['X-Atomgit-Event'] : '';
log_message('INFO', "处理事件类型: {$event}");

if ($event !== 'pull_requests') {
    log_message('INFO', "事件类型不处理。");
    echo json_encode(['message' => 'Event not handled']);
    exit;
}

// 获取 JSON 负载
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);

if (json_last_error() !== JSON_ERROR_NONE) {
    log_message('ERROR', "解析 Webhook 负载失败: " . json_last_error_msg());
    http_response_code(400);
    echo json_encode(['message' => 'Invalid payload']);
    exit;
}

log_message('DEBUG', "Webhook Payload: {$payload}");

/*
{
    "action": "opened",
    "number": 2,
    "organization": {
        "id": "67458b1d34b3fe1dfd989473",
        "login": "NutAtomgit"
    },
    "pull_request": {
        "_links": {
            "html": {
                "href": "https://atomgit.com/nutatomgit/dco_xyg/change/2"
            }
        },
        "additions": 1,
        "base": {
            "label": "NutAtomgit:master",
            "ref": "master",
            "repo": {
                "allow_forking": true,
                "allow_rebase_merge": true,
                "allow_squash_merge": true,
                "archived": false,
                "clone_url": "https://atomgit.com/nutatomgit/dco_xyg.git",
                "created_at": "2025-01-01T20:01:52Z",
                "default_branch": "master",
                "description": "DCO Webhook PHP 应用程序 (Apache + PHP + PHPMailer + SMTP + MySQL + PDO)",
                "disabled": false,
                "fork": false,
                "forks": 0,
                "forks_count": 0,
                "full_name": "nutatomgit/dco_xyg",
                "git_url": "git://atomgit.com/nutatomgit/dco_xyg.git",
                "homepage": "https://atomgit.com/nutatomgit/dco_xyg",
                "html_url": "https://atomgit.com/nutatomgit/dco_xyg",
                "id": 37068902,
                "is_template": false,
                "language": "{\"PHP\":\"{\\\"blank\\\":697,\\\"code\\\":4438,\\\"nFiles\\\":11,\\\"comment\\\":3004}\",\"Markdown\":\"{\\\"blank\\\":66,\\\"code\\\":217,\\\"nFiles\\\":1,\\\"comment\\\":0}\"}",
                "master_branch": "master",
                "name": "dco_xyg",
                "organization": "NutAtomgit",
                "owner": {
                    "html_url": "https://atomgit.com/NutAtomgit",
                    "id": "67458b1d34b3fe1dfd989473",
                    "name": "NutAtomgit",
                    "type": "Organization"
                },
                "private": false,
                "public": true,
                "size": 0.4375,
                "ssh_url": "git@atomgit.com:nutatomgit/dco_xyg.git",
                "stargazers_count": 1,
                "updated_at": "2025-01-24T03:38:26Z",
                "url": "https://atomgit.com/nutatomgit/dco_xyg",
                "visibility": "public"
            },
            "sha": "5e68f3a065279ae6599b5217122ddef4e309ceed",
            "user": {
                "html_url": "https://atomgit.com/NutAtomgit",
                "id": "67458b1d34b3fe1dfd989473",
                "name": "NutAtomgit",
                "type": "Organization"
            }
        },
        "body": "",
        "changed_files": 1,
        "comments": 0,
        "commits": 1,
        "created_at": "2025-01-24T03:38:25Z",
        "deletions": 0,
        "diff_url": "https://atomgit.com/nutatomgit/dco_xyg/change/2/diffs",
        "head": {
            "label": "NutAtomgit:test",
            "ref": "test",
            "repo": {
                "allow_forking": true,
                "allow_rebase_merge": true,
                "allow_squash_merge": true,
                "archived": false,
                "clone_url": "https://atomgit.com/nutatomgit/dco_xyg.git",
                "created_at": "2025-01-01T20:01:52Z",
                "default_branch": "master",
                "description": "DCO Webhook PHP 应用程序 (Apache + PHP + PHPMailer + SMTP + MySQL + PDO)",
                "disabled": false,
                "fork": false,
                "forks": 0,
                "forks_count": 0,
                "full_name": "nutatomgit/dco_xyg",
                "git_url": "git://atomgit.com/nutatomgit/dco_xyg.git",
                "homepage": "https://atomgit.com/nutatomgit/dco_xyg",
                "html_url": "https://atomgit.com/nutatomgit/dco_xyg",
                "id": 37068902,
                "is_template": false,
                "language": "{\"PHP\":\"{\\\"blank\\\":697,\\\"code\\\":4438,\\\"nFiles\\\":11,\\\"comment\\\":3004}\",\"Markdown\":\"{\\\"blank\\\":66,\\\"code\\\":217,\\\"nFiles\\\":1,\\\"comment\\\":0}\"}",
                "master_branch": "master",
                "name": "dco_xyg",
                "organization": "NutAtomgit",
                "owner": {
                    "html_url": "https://atomgit.com/NutAtomgit",
                    "id": "67458b1d34b3fe1dfd989473",
                    "name": "NutAtomgit",
                    "type": "Organization"
                },
                "private": false,
                "public": true,
                "size": 0.4375,
                "ssh_url": "git@atomgit.com:nutatomgit/dco_xyg.git",
                "stargazers_count": 1,
                "updated_at": "2025-01-24T03:38:26Z",
                "url": "https://atomgit.com/nutatomgit/dco_xyg",
                "visibility": "public"
            },
            "sha": "ccc83c3ce3de15e7f2a7fd0d68102c2123d33379",
            "user": {
                "html_url": "https://atomgit.com/NutAtomgit",
                "id": "67458b1d34b3fe1dfd989473",
                "name": "NutAtomgit",
                "type": "Organization"
            }
        },
        "html_url": "https://atomgit.com/nutatomgit/dco_xyg/change/2",
        "id": "af4c95a41d11481cb70e1aa2c41f35a1",
        "mergeable": true,
        "merged": false,
        "number": 2,
        "patch_url": "https://atomgit.com/nutatomgit/dco_xyg/change/2/commits",
        "rebaseable": true,
        "review_comments": 0,
        "state": "open",
        "title": "更新 README.md",
        "updated_at": "2025-01-24T03:38:25Z",
        "user": {
            "avatar_url": "https://file.atomgit.com/uploads/user/default.png",
            "html_url": "https://atomgit.com/g310773517",
            "id": "665b28b618758ec0d1c55999",
            "login": "g310773517",
            "type": "User"
        }
    },
    "repository": {
        "allow_forking": true,
        "archived": false,
        "clone_url": "https://atomgit.com/nutatomgit/dco_xyg.git",
        "created_at": "2025-01-01T20:01:52Z",
        "default_branch": "master",
        "description": "DCO Webhook PHP 应用程序 (Apache + PHP + PHPMailer + SMTP + MySQL + PDO)",
        "disabled": false,
        "fork": false,
        "forks": 0,
        "forks_count": 0,
        "full_name": "nutatomgit/dco_xyg",
        "git_url": "git://atomgit.com/nutatomgit/dco_xyg.git",
        "homepage": "https://atomgit.com/nutatomgit/dco_xyg",
        "html_url": "https://atomgit.com/nutatomgit/dco_xyg",
        "id": 37068902,
        "is_template": false,
        "language": "{\"PHP\":\"{\\\"blank\\\":697,\\\"code\\\":4438,\\\"nFiles\\\":11,\\\"comment\\\":3004}\",\"Markdown\":\"{\\\"blank\\\":66,\\\"code\\\":217,\\\"nFiles\\\":1,\\\"comment\\\":0}\"}",
        "master_branch": "master",
        "name": "dco_xyg",
        "organization": "NutAtomgit",
        "owner": {
            "html_url": "https://atomgit.com/NutAtomgit",
            "id": "67458b1d34b3fe1dfd989473",
            "name": "NutAtomgit",
            "type": "Organization"
        },
        "private": false,
        "size": 0.4375,
        "ssh_url": "git@atomgit.com:nutatomgit/dco_xyg.git",
        "stargazers_count": 1,
        "updated_at": "2025-01-24T03:29:56Z",
        "url": "https://atomgit.com/nutatomgit/dco_xyg",
        "visibility": "public"
    },
    "sender": {
        "avatar_url": "https://file.atomgit.com/uploads/user/default.png",
        "html_url": "https://atomgit.com/g310773517",
        "id": "665b28b618758ec0d1c55999",
        "login": "g310773517",
        "type": "User"
    }
}
*/
// 获取推送信息
$repo  = isset($data['pull_request']['base']['repo']['full_name']) ? $data['pull_request']['base']['repo']['full_name'] : '';
$owner = isset($data['pull_request']['base']['repo']['owner']['name']) ? $data['pull_request']['base']['repo']['owner']['name'] : '';
$commit = isset($data['pull_request']['head']['sha']) ? $data['pull_request']['head']['sha'] : '';
$pusher = isset($data['sender']['login']) ? $data['sender']['login'] : '';
$pull_number = isset($data['number']) ? $data['number'] : '';
log_message('INFO', "处理仓库: {$repo}, commit:{$commit}, Pusher: {$pusher}");

// 获取推送信息
$before = isset($data['before']) ? $data['before'] : '';
$after = isset($data['after']) ? $data['after'] : '';

// 从 tokens.json 文件中获取 access_token
$tokens = json_decode(file_get_contents("tokens.json"), true);
if (json_last_error() !== JSON_ERROR_NONE) {
    log_message('ERROR', "解析 tokens.json 失败: " . json_last_error_msg());
    http_response_code(500);
    echo json_encode(['message' => 'Failed to parse tokens.json']);
    exit;
}

$access_token = isset($tokens['access_token']) ? $tokens['access_token'] : '';
if (empty($access_token)) {
    log_message('ERROR', "access_token 未找到");
    http_response_code(500);
    echo json_encode(['message' => 'Access token not found']);
    exit;
}

// 获取提交信息
$commit_url = "https://api.atomgit.com/repos/{$repo}/commits/{$commit}";
$options = [
    'http' => [
        'header' => "Authorization: Bearer {$access_token}\r\n",
        'method' => 'GET',
    ],
];

$context = stream_context_create($options);
$response = file_get_contents($commit_url, false, $context);

if ($response === FALSE) {
    log_message('ERROR', "获取提交信息失败: {$commit_url}");
    http_response_code(500);
    echo json_encode(['message' => 'Failed to fetch commit information']);
    exit;
}

$commit_data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
    log_message('ERROR', "解析提交信息失败: " . json_last_error_msg());
    http_response_code(500);
    echo json_encode(['message' => 'Failed to parse commit information']);
    exit;
}

$commit_author_email = isset($commit_data['commit']['author']['email']) ? $commit_data['commit']['author']['email'] : '';
$commit_author_name = isset($commit_data['commit']['author']['name']) ? $commit_data['commit']['author']['name'] : '';
$commit_message = isset($commit_data['message']) ? $commit_data['message'] : '';

log_message('INFO', "Commit Author Email: {$commit_author_email}, Author Name: {$commit_author_name}, Message: {$commit_message}");

$lines = explode("\n", $commit_message);
$dco_found = false;
$dco_author_name = '';
$dco_author_email = '';

foreach ($lines as $line) {
    // 去除行首和行尾的空白字符（包括空格和换行符）
    $trimmed_line = trim($line);

    // 检查行是否以 "Signed-off-by:" 开头
    if (strpos($trimmed_line, 'Signed-off-by:') === 0) {
        // 去除 "Signed-off-by" 及其后面的空格
        $signature = substr($trimmed_line, strlen('Signed-off-by:') + 1);
        $signature = trim($signature);

        // 查找邮箱地址（邮箱地址是最后一个不包含空格的部分）
        $email_pos = strrpos($signature, ' ');
        if ($email_pos !== false) {
	    $dco_found = true;
            // 分割名字和邮箱
            $dco_author_name = trim(substr($signature, 0, $email_pos));
            $dco_author_email = trim(substr($signature, $email_pos + 1));
	    log_message('DEBUG', "找到有效的 DCO 签名：姓名 = $dco_author_name, 邮箱 = $dco_author_email\n");
            // 如果只需要找到第一个签名就退出循环，可以使用 break
            break;
        }
    }
}
$labels = [];
$subject = isset($env['EMAIL_SUBJECT']) ? $env['EMAIL_SUBJECT'] : "DCO 验证失败通知";
$commit_msg = "";

if ($dco_found) {
    if ($dco_author_name !== $commit_author_name || $dco_author_email !== $commit_author_email) {
        log_message('INFO', "DCO 信息与提交作者信息不一致。");
	$comment_msg = "DCO 信息与提交作者信息不一致。\ncommit:{$commit}\n提交作者：{$commit_author_name} {$commit_author_email}\nDCO信息：{$dco_author_name} {$dco_author_email}";
	$labels[] = 'DCO Failed';
	// 发送邮件通知
	sendEmail($subject, $comment_msg, $commit_author_email);
    } else {
        log_message('INFO', "DCO 信息与提交作者信息一致。");
        $commit_msg = "DCO 信息与提交作者信息一致。";
	$labels[] = 'DCO Signed';
    }
} else {
    log_message('INFO', "未找到 DCO 信息。");
    $comment_msg = "未找到 DCO 信息。\ncommit:{$commit}\n提交作者：{$commit_author_name} {$commit_author_email}\nDCO信息：无";
    // 发送邮件通知
    sendEmail($subject, $comment_msg, $commit_author_email);
    $labels[] = 'DCO Failed';
}
sendPRLabels($repo, $pull_number, $access_token, $labels);
sendPRComment($repo, $pull_number, $access_token, $comment_msg, $commit, $access_token);

/**
 * 发送邮件通知
 *
 * @param string $subject
 * @param string $body
 * @param string $recipient_email
 * @return void
 */
function sendEmail($subject, $body, $recipient_email)
{
    global $env;

    if (empty($recipient_email)) {
        log_message('WARNING', '未指定邮件接收人，跳过发送邮件。');
        return;
    }

    log_message('INFO', "send mail notify to {$recipient_email}:\n{$body}");

    // 调用 send_email.php 异步发送邮件
    $cmd = "php " . escapeshellarg(__DIR__ . '/send_email.php') . " " . escapeshellarg($subject) . " " . escapeshellarg($body) . " " . escapeshellarg($recipient_email) . " > /dev/null 2>&1 &";
    shell_exec($cmd);
}

function sendPRLabels($repo, $pull_number, $token, $labels) {
    $url = "https://api.atomgit.com/repos/{$repo}/pulls/{$pull_number}/labels";

    // 准备 POST 数据
    $data = [
        'label_name_list' => $labels
    ];
    $jsonData = json_encode($data);

    // 初始化 cURL 会话
    $ch = curl_init($url);

    // 设置 cURL 选项
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应结果而不是直接输出
    curl_setopt($ch, CURLOPT_POST, true); // 发送 POST 请求
    curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); // POST 数据
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $token
    ]);

    // 执行 cURL 请求并获取响应
    $response = curl_exec($ch);

    // 检查 cURL 错误
    if (curl_errno($ch)) {
        log_message('Error', 'cURL Error: ' . curl_error($ch));
        curl_close($ch);
        return new Exception('cURL Error');
    }

    // 获取 HTTP 状态码
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    // 关闭 cURL 会话
    curl_close($ch);

    // 输出调试信息（可选）
    log_message('INFO', "send $url HTTP Status Code: " . $statusCode);

    // 检查 HTTP 状态码
    if ($statusCode >= 200 && $statusCode < 400) {
        return null; // 或 return true; 表示成功
    } else {
        // 解析并返回错误信息
        $responseArray = json_decode($response, true);
        $errorMessage = isset($responseArray['message']) ? $responseArray['message'] : 'Unknown error';
        return new Exception('Error, status: ' . $responseArray['status'] ?? 'N/A' . ', code: ' . $statusCode . ', message: ' . $errorMessage);
    }
}

function sendPRComment($repo, $pull_number, $body, $commit_id, $token)
{
    $url = "https://api.atomgit.com/repos/{$repo}/pulls/{$pull_number}/comments";

    // 准备 POST 数据
    $data = [
        'body'    => $body,
	'commit_id' => $commit_id
    ];
    $jsonData = json_encode($data);

    // 初始化 cURL 会话
    $ch = curl_init($url);

    // 设置 cURL 选项
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);  // 返回响应结果而不是直接输出
    curl_setopt($ch, CURLOPT_POST, true);            // 发送 POST 请求
    curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData); // POST 数据
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Authorization: Bearer ' . $token,
    ]);

    // 执行 cURL 请求并获取响应
    $response = curl_exec($ch);

    // 检查 cURL 错误
    if (curl_errno($ch)) {
        error_log('cURL Error: ' . curl_error($ch));
        curl_close($ch);
        return new Exception('cURL Error');
    }

    // 获取 HTTP 状态码
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    // 关闭 cURL 会话
    curl_close($ch);

    // 输出调试信息（可选）
    log_message('INFO', "$url HTTP Status Code: " . $statusCode);

    // 检查 HTTP 状态码
    if ($statusCode >= 200 && $statusCode < 400) {
        return null; // 或 return true; 表示成功
    } else {
        // 解析响应体（如果需要）
        // $responseArray = json_decode($response, true);

        // 返回错误信息
        // 注意：这里我们简单地返回一个异常，但实际应用中可能需要根据响应体中的错误信息提供更详细的反馈
        return new Exception('Error, status code: ' . $statusCode . ', response: ' . $response);
    }
}
